#include "wl_file.h"

#include "wl_trace.h"

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
#    include <sfs.h>
static const char s_disk[] = "D:";
#elif defined(WL_OS_QUECTEL)
#    include "ql_fs.h"
#    include <stdlib.h>
static const char s_disk[] = "U:";
#else
#    include <sys/stat.h>
#    include <unistd.h>
static const char s_disk[] = ".";
#endif

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
wl_xip_callback s_xip_lock_callback;
wl_xip_callback s_xip_unlock_callback;

static void path_asc_to_ucs2(wl_char* path_asc, wl_uint16* path_ucs2) {
    wl_int32 i       = 0;
    wl_int32 pathlen = strlen(path_asc);

    for (i = 0; i < pathlen; i++) {
        if ('/' == path_asc[i]) {
            path_ucs2[i] = '\\';
        } else {
            path_ucs2[i] = path_asc[i];
        }
    }
    path_ucs2[i] = 0;
}

static void create_full_path(const wl_char* path, OUT wl_char* full_path) {
    sprintf(full_path, "%s/%s", s_disk, path);
}

wl_int32 wl_full_path(wl_char* path, wl_char* full_path) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    if (-1 == wl_access(path, 0)) {
        wl_char full_path_asc[128];
        sprintf(full_path_asc, "%s/%s", s_disk, path);
        path_asc_to_ucs2(full_path_asc, (uint16*)full_path);
    }
#elif defined(WL_OS_QUECTEL)
    create_full_path(path, full_path);
#else
    strcpy(full_path, path);
#endif
    return 0;
}

wl_int32 wl_mkdir(const wl_char* path) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    if (-1 == wl_access(path, 0)) {
        uint16  full_path_ucs2[128];
        wl_char full_path_asc[128];
        sprintf(full_path_asc, "%s/%s", s_disk, path);
        path_asc_to_ucs2(full_path_asc, full_path_ucs2);
        if (SFS_NO_ERROR != SFS_CreateDirectory(full_path_ucs2)) {
            return -1;
        }
    }
#elif defined(WL_OS_QUECTEL)
    wl_char full_path[128];
    create_full_path(path, full_path);
    xip_lock();
    if (LFS_ERR_OK != ql_mkdir(full_path, 0)) {
        xip_unlock();
        return -1;
    }
    xip_unlock();
#else
    if (-1 == wl_access(path, 0)) {
        return mkdir(path, 0666);
    }
#endif
    return 0;
}

wl_int32 wl_rmdir(const wl_char* path) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    if (0 == wl_access(path, 0)) {
        uint16  full_path_ucs2[128];
        wl_char full_path_asc[128];
        sprintf(full_path_asc, "%s/%s", s_disk, path);
        path_asc_to_ucs2(full_path_asc, full_path_ucs2);
        if (SFS_NO_ERROR != SFS_DeleteDirectory(full_path_ucs2)) {
            return -1;
        }
    }
#elif defined(WL_OS_QUECTEL)
    wl_char full_path[128];
    create_full_path(path, full_path);
    xip_lock();
    QDIR* dir = ql_opendir(full_path);
    if (dir) {
        struct lfs_info info;
        memset(&info, 0, sizeof(struct lfs_info));
        while (ql_readdir(dir, &info) == LFS_ERR_OK) {
            if (info.type == LFS_TYPE_DIR) {
                wl_rmdir(info.name);
            } else {
                ql_remove(info.name);
            }
        }
        ql_remove(full_path);
        ql_closedir(dir);
        xip_unlock();
    } else {
        xip_unlock();
        return -1;
    }

#else
    if (0 == wl_access(path, 0)) {
        return rmdir(path);
    }
#endif
    return 0;
}

wl_int32 wl_remove(const wl_char* path) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    SFS_ERROR_E error = SFS_NO_ERROR;
    uint16      full_path_ucs2[128];
    wl_char     full_path_asc[128];
    sprintf(full_path_asc, "%s/%s", s_disk, path);
    path_asc_to_ucs2(full_path_asc, full_path_ucs2);
    error = SFS_DeleteFile(full_path_ucs2, wl_null);
    if (SFS_NO_ERROR != error && SFS_ERROR_IO_PENDING != error) {
        WLLOGW("SFS_RenameFile error %d", error);
        return -1;
    }
    return 0;
#elif defined(WL_OS_QUECTEL)
    wl_char  full_path[128];
    wl_int32 ret = -1;
    create_full_path(path, full_path);
    xip_lock();
    ret = ql_remove(full_path);
    xip_unlock();
    return ret;
#else
    return remove(path);
#endif
}

wl_int32 wl_rename(const wl_char* src_path, const wl_char* dst_path) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    SFS_ERROR_E error = SFS_NO_ERROR;
    uint16      full_src_path_ucs2[128];
    wl_char     full_src_path_asc[128];
    uint16      full_dst_path_ucs2[128];
    wl_char     full_dst_path_asc[128];
    sprintf(full_src_path_asc, "%s/%s", s_disk, src_path);
    path_asc_to_ucs2(full_src_path_asc, full_src_path_ucs2);
    sprintf(full_dst_path_asc, "%s/%s", s_disk, dst_path);
    path_asc_to_ucs2(full_dst_path_asc, full_dst_path_ucs2);
    error = SFS_RenameFile(full_src_path_ucs2, full_dst_path_ucs2, wl_null);
    if (SFS_NO_ERROR != error && SFS_ERROR_IO_PENDING != error) {
        WLLOGW("SFS_RenameFile error %d", error);
        return -1;
    }
    return 0;
#elif defined(WL_OS_QUECTEL)
    wl_char  src_full_path[128];
    wl_char  dst_full_path[128];
    wl_int32 ret = -1;
    create_full_path(src_path, src_full_path);
    create_full_path(dst_path, dst_full_path);
    xip_lock();
    ret = ql_rename(src_full_path, dst_full_path);
    xip_unlock();
    return ret;
#else
    return rename(src_path, dst_path);
#endif
}

wl_int32 wl_access(const wl_char* path, wl_int32 mode) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    uint16          full_path_ucs2[128];
    wl_char         full_path_asc[128];
    SFS_HANDLE      sfs_handle;
    SFS_FIND_DATA_T find_data;
    sprintf(full_path_asc, "%s/%s", s_disk, path);
    path_asc_to_ucs2(full_path_asc, full_path_ucs2);
    sfs_handle = SFS_FindFirstFile(full_path_ucs2, &find_data);
    if (WL_INVALID_FHANDLE != sfs_handle) {
        SFS_FindClose(sfs_handle);
        return 0;
    }
    return -1;
#elif defined(WL_OS_QUECTEL)
    wl_char  full_path[128];
    wl_int32 ret = -1;
    create_full_path(path, full_path);
    xip_lock();
    ret = ql_access(full_path, mode);
    xip_unlock();
    return ret;
#else
    return access(path, mode);
#endif
}

WL_FHANDLE wl_fopen(const wl_char* path, wl_int32 flags) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    uint16  full_path_ucs2[128];
    wl_char full_path_asc[128];
    sprintf(full_path_asc, "%s/%s", s_disk, path);
    WLLOGI("path %s full_path %s", path, full_path_asc);
    path_asc_to_ucs2(full_path_asc, full_path_ucs2);
    return SFS_CreateFile(full_path_ucs2, flags, wl_null, wl_null);
#elif defined(WL_OS_QUECTEL)
    wl_char* mode = wl_null;
    if (path) {
        wl_char full_path[128];
        QFILE*  handle = WL_INVALID_FHANDLE;
        create_full_path(path, full_path);
        WLLOGI("full_path %s", full_path);
        if (flags & WL_CREAT) {
            mode = "wb+";
        } else if (flags & WL_RDWR) {
            mode = "rb+";
        } else if (flags & WL_RDONLY) {
            mode = "rb";
        } else if (flags & WL_WRONLY) {
            mode = "wb";
        } else if (flags & WL_APPEND) {
            mode = "a+";
        } else {
            return WL_INVALID_FHANDLE;
        }
        xip_lock();
        handle = ql_fopen(full_path, mode);
        xip_unlock();
        return handle;
    }
    return WL_INVALID_FHANDLE;
#else
    return open(path, flags, 0666);
#endif
}

wl_int32 wl_fread(WL_FHANDLE fhandle, OUT void* buf, wl_uint32 count) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    uint32 readlen = 0;
    if (SFS_NO_ERROR != SFS_ReadFile(fhandle, buf, count, &readlen, wl_null)) {
        return 0;
    }
    return readlen;
#elif defined(WL_OS_QUECTEL)
    wl_int32 ret = -1;
    xip_lock();
    ret = ql_fread(buf, 1, count, fhandle);
    xip_unlock();
    return ret;
#else
    return read(fhandle, buf, count);
#endif
}

wl_int32 wl_fwrite(WL_FHANDLE fhandle, void* buf, wl_uint32 count) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    uint32 writelen = 0;
    if (SFS_NO_ERROR != SFS_WriteFile(fhandle, buf, count, &writelen, wl_null)) {
        return 0;
    }
    return writelen;
#elif defined(WL_OS_QUECTEL)
    wl_int32 ret = -1;
    xip_lock();
    ret = ql_fwrite(buf, 1, count, fhandle);
    xip_unlock();
    return ret;
#else
    return write(fhandle, buf, count);
#endif
}

wl_int32 wl_fclose(WL_FHANDLE fhandle) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    return SFS_CloseFile(fhandle);
#elif defined(WL_OS_QUECTEL)
    wl_int32 ret = -1;
    xip_lock();
    ret = ql_fclose(fhandle);
    xip_unlock();
    return ret;
#else
    return close(fhandle);
#endif
}

wl_int32 wl_fseek(WL_FHANDLE fhandle, wl_int32 offset, wl_int32 whence) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    if (SFS_NO_ERROR != SFS_SetFilePointer(fhandle, offset, whence)) {
        return -1;
    }
    return offset;
#elif defined(WL_OS_QUECTEL)
    wl_int32 ret = -1;
    xip_lock();
    ret = ql_fseek(fhandle, offset, whence);
    xip_unlock();
    return ret;
#else
    return lseek(fhandle, offset, whence);
#endif
}

wl_ssize wl_fsize(WL_FHANDLE fhandle) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    uint32 size = 0;
    if (SFS_ERROR_NONE == SFS_GetFileSize(fhandle, &size)) {
        return size;
    }
    return 0;
#elif defined(WL_OS_QUECTEL)
    wl_int32 ret = -1;
    xip_lock();
    ret = ql_fsize(fhandle);
    xip_unlock();
    return ret;
#else
    struct stat statbuf;
    if (0 == fstat(fhandle, &statbuf)) {
        WLLOGT("fsize %d", statbuf.st_size);
        return statbuf.st_size;
    }
    return 0;
#endif
}

wl_int32 wl_set_xip_callback(wl_xip_callback lock_callback, wl_xip_callback unlock_callback) {
    s_xip_lock_callback   = lock_callback;
    s_xip_unlock_callback = unlock_callback;
    return 0;
}

wl_int32 xip_lock(void) {
    if (s_xip_lock_callback) {
        s_xip_lock_callback();
    }
    return 0;
}

wl_int32 xip_unlock(void) {
    if (s_xip_unlock_callback) {
        s_xip_unlock_callback();
    }
    return 0;
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
